/**
 * 
 */
package br.com.grafos;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;

/**
 * Classe que tem a responsabilidade de fabricar/construir o grafo.
 * 
 * Efetua a leitura do arquivo texto, monta os vértices e seus arcos.
 * 
 * Também origina vértice raíz e anti-raíz caso o grafo possua base ou
 * anti-base.
 * 
 * @author moises
 */
public class FabricaGrafo {

    /**
     * Inicialização do objeto grafo a ser construído
     */
    private Grafo grafo = new Grafo();

    /**
     * Conjunto com o nome dos vértices base
     */
    private Set<String> base = new HashSet<>();

    /**
     * Conjunto com o nome dos vértices anti base
     */
    private Set<String> antiBase = new HashSet<>();

    /**
     * Constante com o nome do nodo raíz
     */
    private static final String RAIZ = "RAÍZ";

    /**
     * Constante com o nome do nodo anti raíz
     */
    private static final String ANTI_RAIZ = "ANTI-RAÍZ";

    /**
     * Quantidade de nodos do grafo
     */
    private Integer quantidadeVertices;

    public Grafo construir(String nomeArquivo) throws IOException {
	BufferedReader inputStream = null;

	try {
	    // abre stream para leitura do arquivo texto
	    inputStream = new BufferedReader(new FileReader(nomeArquivo));

	    String linha;

	    // lê a linha com a quantidade dos nodos do grafo
	    quantidadeVertices = new Integer(inputStream.readLine().trim());

	    // fabrica nodos do grafo
	    fabricarVertices(quantidadeVertices);

	    // loop para montagem dos arcos do grafo
	    while ((linha = inputStream.readLine()) != null) {
		StringTokenizer st = new StringTokenizer(linha);
		// lê nome do nodo emissor do arco
		String origem = st.nextToken();

		// lê nome do nodo receptor do arco
		String destino = st.nextToken();

		// lê o custo associado ao arco
		String custo = st.nextToken();

		// busca pelo nome o nodo emissor do arco previamente fabricado
		Vertice verticeOrigem = Util.buscarVertice(this.grafo, origem);

		// busca pelo nome o nodo receptor do arco previamente fabricado
		Vertice verticeDestino = Util
			.buscarVertice(this.grafo, destino);

		// adiciona relacionamento (arco) entre o nodo emissor e
		// receptor
		// aqui é adicionado um vizinho para o nodo emissor
		verticeOrigem.getVizinhos().add(
			new Arco(verticeDestino, Integer.parseInt(custo)));

		// remove do conjunto - os nomes restantes na coleção serão os
		// nodos base (ou raíz)
		this.base.remove(destino);

		// remove do conjunto - os nomes restantes na coleção serão os
		// nodos anti base (ou anti raíz)
		this.antiBase.remove(origem);
	    }
	} finally {
	    if (inputStream != null) {
		// fecha o stream com o arquivo texto
		inputStream.close();
	    }
	}

	// testa se grafo possui nodos base ou nodo raíz
	if (this.base.size() > 1) {
	    // grafo possui base - poranto deverá ser criado nodo raíz para o
	    // grafo
	    this.grafo.getVertices().add(new Vertice(RAIZ));
	    this.grafo.setNomeVerticeRaiz(RAIZ);
	    Vertice raiz = this.grafo.getRaiz();
	    // criação dos arcos com incidência nos nodos base
	    for (String nomeVerticeBase : this.base) {
		// busca no grafo vértice pelo nome
		Vertice verticeBase = Util.buscarVertice(this.grafo,
			nomeVerticeBase);
		// adiciona arco apontando para um nodo base do grafo
		raiz.getVizinhos().add(new Arco(verticeBase, 0));
	    }
	} else {
	    // grafo possui raíz - seta no grafo o nome de seu nodo raíz
	    this.grafo.setNomeVerticeRaiz(this.base.iterator().next());
	}

	// testa se grafo possui nodos anti base ou nodo anti raíz
	if (this.antiBase.size() > 1) {
	    // grafo possui nodos anti base
	    this.grafo.getVertices().add(new Vertice(ANTI_RAIZ));
	    this.grafo.setNomeVerticeAntiRaiz(ANTI_RAIZ);
	    // criar arcos apontando para anti-raiz
	    for (String nomeVerticeAntiBase : this.antiBase) {
		Vertice verticeAntiBase = Util.buscarVertice(this.grafo,
			nomeVerticeAntiBase);
		verticeAntiBase.getVizinhos().add(
			new Arco(this.grafo.getAntiRaiz(), 0));
	    }
	} else {
	    // grafo possui nodo anti raíz
	    this.grafo.setNomeVerticeAntiRaiz(this.antiBase.iterator().next());
	}

	// retorna o grafo construído a partir das informações do arquivo texto
	return this.grafo;
    }

    /**
     * Método que fabrica/constrói os vértices do grafo baseado na quantidade
     * informada no arquivo texto
     * 
     * @param quantidadeVertices
     */
    private void fabricarVertices(Integer quantidadeVertices) {
	// cria nodos de 1 até a quantidade informada
	for (int i = 1; i <= quantidadeVertices; i++) {
	    String indice = new Integer(i).toString();
	    // adiciona nodo ao grafo
	    this.grafo.getVertices().add(new Vertice(indice));
	    this.base.add(indice);
	    this.antiBase.add(indice);
	}
    }

}
